package com.zhz.common.utils.enums;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Maps;
import com.zhz.common.enums.BizResponseEnum;
import com.zhz.common.tool.mybatisplus.base.core.enums.EnumValueResolver;
import com.zhz.common.utils.cache.CacheUtils;
import com.zhz.common.utils.functions.FunctionUtils;
import org.springframework.lang.Nullable;

import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

/**
 * EnumUtils
 * <p>
 * 枚举工具类
 *
 * @author zhouhengzhe
 * @see EnumValueResolver
 */
public class EnumUtils {


    /**
     * 根据 value 查询枚举（将 value 转为字符串再进行匹配）
     */
    @Nullable
    public static <E extends Enum<E> & EnumValueResolver<E, ?>> E getByStringCode(Class<E> enumType, Object code) {
        if (isNullCode(code)) {
            return null;
        }

        String codeStr = code.toString();
        E[] values = enumType.getEnumConstants();
        for (E e : values) {
            if (e.getStringCode().equals(codeStr)) {
                return e;
            }
        }
        return null;
    }


    /**
     * 根据 code 查询枚举
     */
    @Nullable
    public static <E extends Enum<E>> E getByCode(Class<E> enumType, Object code, Function<E, ?> function) {
        return getByCode(enumType, code, function, (E) null);
    }


    /**
     * 根据 code 查询枚举
     *
     * @param enumType    枚举类型
     * @param code        解析值
     * @param function    解析函数
     * @param defaultCode 默认值
     */
    @Nullable
    public static <E extends Enum<E>> E getByCode(Class<E> enumType, Object code, Function<E, ?> function, @Nullable E defaultCode) {
        if (isNullCode(code)) {
            return defaultCode;
        }

        E[] codes = enumType.getEnumConstants();
        for (E e : codes) {
            if (function.apply(e).equals(code)) {
                return e;
            }
        }
        return defaultCode;
    }


    /**
     * 根据 value 查询枚举
     */
    @Nullable
    public static <E extends Enum<E> & EnumValueResolver<E, ?>> E getByCode(Class<E> enumType, Object code) {
        return getByCode(enumType, code, (E) null);
    }


    /**
     * 根据 value 查询枚举
     */
    public static <E extends Enum<E> & EnumValueResolver<E, ?>> E getByCode(Class<E> enumType, Object code, E defaultCode) {
        if (isNullCode(code)) {
            return defaultCode;
        }

        E[] values = enumType.getEnumConstants();
        for (E e : values) {
            if (e.getCode().equals(code)) {
                return e;
            }
        }
        return defaultCode;
    }


    /**
     * 根据 value 查询枚举
     */
    public static <E extends Enum<E>> E requiredByCode(Class<E> enumType, Object code, Function<E, ?> function) {
        E e = getByCode(enumType, code, function);
        if (Objects.isNull(e)) {
            throw new IllegalStateException(StrUtil.format("不支持的枚举[{}]值: {}", enumType.getSimpleName(), code));
        }
        return e;
    }


    public static <E extends Enum<E> & EnumValueResolver<E, ?>> E requiredByCode(Class<E> enumType, Object code) {
        E e = getByCode(enumType, code);
        BizResponseEnum.NOT_SUPPORT_ENUM.assertFail(e);
        return e;
    }


    /**
     * 根据 value 查询枚举（并且必须是候选值之一）
     */
    public static <E extends Enum<E>> E requiredByCode(Class<E> enumType, Object code, Function<E, ?> function, E... candidates) {
        E e = requiredByCode(enumType, code, function);
        return candidate(enumType, e, code, candidates);
    }


    /**
     * 根据 value 查询枚举（并且必须是候选值之一）
     */
    public static <E extends Enum<E> & EnumValueResolver<E, ?>> E requiredByCode(Class<E> enumType, Object code, E... candidates) {
        E e = requiredByCode(enumType, code);
        return candidate(enumType, e, code, candidates);
    }


    /**
     * 判断是否为空值
     */
    private static boolean isNullCode(Object code) {
        if (Objects.isNull(code)) {
            return true;
        }

        if (code instanceof String && StrUtil.isBlank(code.toString())) {
            return true;
        }

        return false;
    }


    @SafeVarargs
    private static <E extends Enum<E>> E candidate(Class<E> enumType, E e, Object code, E... candidates) {
        for (E candidate : candidates) {
            if (e == candidate) {
                return e;
            }
        }
        throw new IllegalStateException(StrUtil.format("不支持的枚举[{}]值: {}", enumType.getSimpleName(), code));
    }


    /**
     * 判断枚举值是否存在
     */
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> boolean exists(Class<E> enumType, V code) {
        if (Objects.isNull(code)) {
            return false;
        }
        return Objects.nonNull(getByCode(enumType, code));
    }


    /**
     * 获取枚举的 codeMap
     *
     * @param enumType 枚举类型
     * @return <字典值, 字典描述>
     */
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> Map<V, String> getCodeMap(Class<E> enumType) {
        E[] codes = enumType.getEnumConstants();
        Map<V, String> codeMap = Maps.newHashMapWithExpectedSize(codes.length);
        for (E code : codes) {
            codeMap.put(code.getCode(), code.getMessage());
        }
        return Collections.unmodifiableMap(codeMap);
    }


    /**
     * 获取枚举的 codeMap（会进行弱缓存）
     *
     * @param enumType 枚举类型
     * @return <字典值, 字典描述>
     */
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> Map<V, String> getCodeMapCache(Class<E> enumType) {
        return CacheUtils.weak().get(enumType.getName(), () -> getCodeMap(enumType));
    }


    /**
     * 获取枚举值描述
     *
     * @param enumType    枚举类型
     * @param code        枚举值
     * @param defaultCode 默认值
     * @return 枚举描述
     */
    @Nullable
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> String getEnumMessage(Class<E> enumType, V code, String defaultCode) {
        if (Objects.isNull(code)) {
            return defaultCode;
        }
        return getCodeMapCache(enumType).getOrDefault(code, defaultCode);
    }


    /**
     * 获取枚举值描述
     *
     * @param enumType 枚举类型
     * @param code     枚举值
     * @return 枚举描述
     */
    @Nullable
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> String getEnumMessage(Class<E> enumType, V code) {
        return getEnumMessage(enumType, code, null);
    }


    /**
     * 获取枚举值描述
     *
     * @param enumType 枚举类型
     * @param code     枚举值
     * @return 枚举描述（不存在则返回原 value）
     */
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> String getEnumCodeMessage(Class<E> enumType, V code) {
        String desc = getEnumMessage(enumType, code);
        if (Objects.nonNull(desc)) {
            return desc;
        }
        return FunctionUtils.map(code, String::valueOf);
    }


    /**
     * 判断目标值是否是枚举值
     */
    @SafeVarargs
    public static <V> boolean isIn(V code, EnumValueResolver<?, V>... enums) {
        Assert.notEmpty(enums);
        return isIn(code, CollUtil.newHashSet(enums));
    }


    /**
     * 判断目标值是否是枚举值
     */
    public static <V> boolean isIn(V code, Set<? extends EnumValueResolver<?, V>> enumSet) {
        Assert.notEmpty(enumSet);
        if (Objects.isNull(code)) {
            return false;
        }

        for (EnumValueResolver<?, V> anEnum : enumSet) {
            if (anEnum.isEquals(code)) {
                return true;
            }
        }
        return false;
    }


}
